package com.spring.security.two;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.jdbc.object.MappingSqlQuery;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.ConfigAttributeEditor;
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.RequestKey;
import org.springframework.security.web.util.AntPathRequestMatcher;
import org.springframework.security.web.util.RequestMatcher;

import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;


public class JdbcFilterInvocationDefinitionSourceFactoryBean extends JdbcDaoSupport implements FactoryBean {

    private String resourceQuery;

    @Override
    public Object getObject() throws Exception {

//        return new DefaultFilterInvocationSecurityMetadataSource(
//                this.getUrlMatcher("/**"),
//                this.buildRequestMap()
//        );
        return null;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    protected Map<String, String> findResources(){
        ResourceMapping resourceMapping = new ResourceMapping(getDataSource(), resourceQuery);
        Map<String, String > resourceMap = new LinkedHashMap<String, String>();
        for(Resource resource : (List<Resource>) resourceMapping.execute()){
            String url = resource.getUrl();
            String role = resource.getRole();

            if(resourceMap.containsKey(url)){
                String value = resourceMap.get(url);
                resourceMap.put(url, value + "," + role);
            } else {
                resourceMap.put(url, role);
            }
        }

        return resourceMap ;
    }


    protected LinkedHashMap<RequestKey, ConfigAttribute> buildRequestMap(){

        LinkedHashMap<RequestKey, ConfigAttribute> requestMap = null;
        requestMap = new LinkedHashMap<>();

        ConfigAttributeEditor editor = new ConfigAttributeEditor();

        Map<String, String> resourceMap = this.findResources();

        for(Map.Entry<String, String> entry : resourceMap.entrySet()) {
            RequestKey key = new RequestKey(entry.getKey(), null);
            editor.setAsText(entry.getValue());
            requestMap.put(key, (ConfigAttribute)editor.getValue());
        }

        return requestMap ;
    }

    protected RequestMatcher getUrlMatcher(String requestURL){
        return new AntPathRequestMatcher(requestURL);
    }

    public void setResourceQuery(String resourceQuery){
        this.resourceQuery = resourceQuery ;
    }

    private class Resource {
        private String url;
        private String role ;

        public Resource(String url ,String role ){
            this.url = url ;
            this.role = role ;
        }

        public String getUrl() {
            return url;
        }

        public String getRole() {
            return role;
        }
    }

    private class ResourceMapping extends MappingSqlQuery {

        protected ResourceMapping (DataSource dataSource, String resourceQuery){
            super(dataSource, resourceQuery);
            compile();
        }

        @Override
        protected Object mapRow(ResultSet resultSet, int i) throws SQLException {

            String url = resultSet.getString(1);
            String role = resultSet.getString(2);
            Resource resource = new Resource(url, role);

            return resource;
        }
    }
}
